#include "dvo.h"
#include <iostream>

enum ARCS
{
	First,
	Second,
	Third
};

struct Aggregatum{
	int first;
	int second;
	int third;
};

class SimpleBB : public dvo::ps::Transceiver < int, int >
{
public:
	SimpleBB(std::function<void(int&)> function) : process_(function){}
protected:
	virtual void Process(int& value) override { process_(value); Send(value);}
private:
	std::function<void(int&)> process_;
};

class Distributor : public  dvo::ps::Detacher < ARCS, Aggregatum, int >
{
public:
	Distributor();
protected:
	virtual void Process(Aggregatum& value) override;
};

Distributor::Distributor() 
{
	Add(First); 
	Add(Second); 
	Add(Third); 
}

void Distributor::Process(Aggregatum& value)
{
	auto f1 = Send(ARCS::First, value.first);
	auto f2 = Send(ARCS::Second, value.second);
	auto f3 = Send(ARCS::Third, value.third);
	f1.get(); f2.get(); f3.get();
	std::cout << "Detach is finished its process";
}


class Uniter : public dvo::ps::Joiner < ARCS, int, int >
{
public:
	Uniter();
protected:
	virtual void Process() override;
private:
	std::thread thread_;
};

Uniter::Uniter()
{
	Add(First);
	Add(Second);
	Add(Third);
}

void Uniter::Process()
{
	auto v1 = Get(ARCS::First);
	auto v2 = Get(ARCS::Second);
	auto v3 = Get(ARCS::Third);
	std::cout << "Uniter is finished its process, the sum is " << v1 + v2 + v3;
}

bool test_joiner()
{
  return true;
}

void run_joiner()
{
    std::function<void(int&)> func = [&](int& value){ std::cout << "sleeping for " << value << "seconds" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(value)); };
	SimpleBB first(func);
	SimpleBB second(func);
	SimpleBB third(func);
	Distributor dist;
	Uniter uni;

	dist.Outputs(ARCS::First)->Connect(first.Input());
	dist.Outputs(ARCS::Second)->Connect(second.Input());
	dist.Outputs(ARCS::Third)->Connect(third.Input());

	first.Output()->Connect(uni.Inputs(ARCS::First));
	second.Output()->Connect(uni.Inputs(ARCS::Second));
	third.Output()->Connect(uni.Inputs(ARCS::Third));

	Aggregatum agr;
    agr.first = 20;
    agr.second = 30;
    agr.third = 40;
	dist.Input()->Transmit(agr);
}